package com.authine.cloudpivot.ext.service.bill.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapBuilder;
import com.alibaba.fastjson.JSON;
import com.authine.cloudpivot.ext.Enum.BillType;
import com.authine.cloudpivot.ext.Enum.InvoiceStatus;
import com.authine.cloudpivot.ext.Enum.TradeOrderType;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.constants.PlatformConst;
import com.authine.cloudpivot.ext.dto.model.bill.Bill;
import com.authine.cloudpivot.ext.dto.model.bill.OrderToBill;
import com.authine.cloudpivot.ext.dto.model.trade.Trade;
import com.authine.cloudpivot.ext.dto.query.BillQuery;
import com.authine.cloudpivot.ext.service.bill.BillService;
import com.authine.cloudpivot.ext.utils.*;
import com.authine.mvp.app.launcher.domain.SelectorFormat;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import com.authine.mvp.app.launcher.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Maps;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Slf4j
public class BillServiceImpl implements BillService {
    NamedParameterJdbcTemplate jdbcTemplate = ApplicationContextUtils.getBean(NamedParameterJdbcTemplate.class);

    /**
     * 根据结算明细ID删除服务费账单 FinancialFee PlatformFee
     *
     * @param ids referenceIds 关联的结算单单明细ID
     */
    @Override
    public void deleteService(Collection<String> ids) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("删除服务费账单");

        Assert.isTrue(!CollectionUtils.isEmpty(ids), "参数不能为空");
        List<String> referenceIds = ids.stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toList());
        List<Bill> feeBills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                .in("reference_id", referenceIds)
                .in("type", Arrays.asList(BillType.FinancialFee.name(),BillType.PlatformFee))
                .queryForPOJOList(Bill.class);

        if (!CollectionUtils.isEmpty(feeBills)) {
            for (Bill feeBill : feeBills) {
                Integer rows = JdbcTemplateUtils.builder(ModelConst.T_BILL).putFieldAndVal("deleted",1)
                        .eq("id", feeBill.getId())
                        .update();
                Assert.isTrue(rows == 1, "删除服务费账单失败");
            }
        }

        stopWatch.stop();
        String prettyPrint = stopWatch.prettyPrint();
        log.info(prettyPrint);
    }

    @Override
    public void createGoodsBill(Collection<OrderToBill> bills) {
        Collection<Bill> billList = new ArrayList<>();
        for (OrderToBill orderToBill : bills){
            Bill bill = new Bill();
            BeanUtils.copyProperties(orderToBill, bill);
            billList.add(bill);
        }
        //构建账单数据,排除已创建的和已退的
        Collection<Bill> persists = this.buildBill(billList);
        //持久化账单数据
        BoServiceUtils.createBo(ModelConst.T_BILL, persists.stream().map(BeanUtil::beanToMap).collect(Collectors.toList()));
        //回写交易可申请开票数量
        List<String> tradeIds = persists.stream().filter(t->t.getType().equals(BillType.Goods.name())).map(Bill::getTrade_id).collect(Collectors.toList());
        tradeService.writeBackAllowedInvoiceQuantity(tradeIds);

    }

    private Collection<Bill> buildBill(Collection<Bill> params) {
        Collection<Bill> results = new ArrayList<>();

        List<Trade> trades = JdbcTemplateUtils.builder(ModelConst.T_TRADE).selectField("*")
                .in("direction", params.stream().map(Bill::getDirection).collect(Collectors.toList()))
                .in("business_type", params.stream().map(Bill::getBusiness_type).collect(Collectors.toList()))
                .in("order_goods_id", params.stream().map(Bill::getOrder_goods_id).collect(Collectors.toList()))
                .queryForPOJOList(Trade.class);
        Assert.isTrue(!CollectionUtils.isEmpty(trades), "未获取到对应的交易");

        for (Bill param : params) {

            //匹配交易列表trades中的数据
            Trade trade = trades.stream().filter(
                    t -> param.getOrder_goods_id().equals(t.getOrder_goods_id())
                            && param.getBusiness_type().equals(t.getBusiness_type())
                            && param.getDirection().equals(t.getDirection())
            ).findAny().orElseThrow(IllegalArgumentException::new);
            //查询已生成的账单数量和金额
            String completeSql = "select IFNULL(sum(quantity),0) quantity, IFNULL(sum(total),0) total from "+JdbcTemplateUtils.getTableName(ModelConst.T_BILL)+" where trade_id=:tradeId" +
                    " and deleted = 0 and (type='Goods' or (type='Refund' and  offset_quantity != 0))";
            Bill completeBill = JdbcTemplateUtils.queryForPOJOList(completeSql, Maps.newHashMap("tradeId", trade.getId()), ModelConst.T_BILL, Bill.class).get(0);

            //当前账单+已生成的完整账单
            BigDecimal finishedQuantity = BigDecimalUtils.add(param.getQuantity(), completeBill.getQuantity());
            Assert.isTrue(finishedQuantity.compareTo(trade.getQuantity()) != 1,
                    "商品编号" + param.getOrder_goods_id() + "需创建账单数量" + param.getQuantity() +
                            "+已完成的账单数量" + completeBill.getQuantity() + "，已经大于交易数量" + trade.getQuantity());

            BigDecimal price = trade.getPrice();
            BigDecimal pretaxPrice = trade.getPretax_price();
            BigDecimal total = BigDecimalUtils.round(BigDecimalUtils.mul(param.getQuantity(), price), 2);

            if (trade.getQuantity().compareTo(finishedQuantity) == 0) {
                //分批情况下的最后一次创建需要重新计算单价
                if (param.getQuantity().compareTo(trade.getQuantity()) != 0) {
                    price = BigDecimalUtils.div(BigDecimalUtils.sub(trade.getTotal(), completeBill.getTotal()), param.getQuantity());
                    pretaxPrice = TaxRateUtils.calculateTaxBefore(price, trade.getTax_rate());
                }

                total = BigDecimalUtils.sub(trade.getTotal(), completeBill.getTotal());
            }

            results.add(Bill.builder()
                    .trade_id(trade.getId())
                    .direction(trade.getDirection())
                    .business_type(trade.getBusiness_type())
                    .customer_order_id(trade.getCustomer_order_id())
                    .vendor_order_id(trade.getVendor_order_id())
                    .order_goods_id(trade.getOrder_goods_id())
                    .reference_id(param.getReference_id())
                    .organization_id(trade.getOrganization_id())
                    .type(param.getType())
                    .summary(JSON.toJSONString(trade.getSummary()))
                    .bill_date(new Date())
                    .organization_name(trade.getOrganization_name())
                    .quantity(param.getQuantity())
                    .offset_quantity(BigDecimal.ZERO)
                    .price(price)
                    .pretax_price(pretaxPrice)
                    .unit(trade.getSummary().getUnit())
                    .tax_rate(trade.getTax_rate())
                    .total(total)
                    .build());
        }
        return results;
    }


    @Override
    public void createRefundGoodsBill(Collection<Bill> bills) {
        log.info("开始创建商品退货账单");
        List<Bill> nonExistBills = bills.stream().filter(t -> !t.getRefundNonExist()).collect(Collectors.toList());
        //退款不需要退货
        if (!CollectionUtils.isEmpty(nonExistBills)) {
            refundNonExist(nonExistBills);
        }

        //退款需要退货
        List<Bill> existBills = bills.stream().filter(Bill::getRefundNonExist).collect(Collectors.toList());
        refundExist(existBills);

        List<String> writeBackTradeIds = new ArrayList<>();
        bills.stream().collect(Collectors.groupingBy(Bill::getDirection)).forEach((direction, billsValue) -> {
            String orderGoodsIds = billsValue.stream().map(Bill::getOrder_goods_id).collect(Collectors.joining(","));
            List<String> tradeIds = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("trade_id")
                    .eq("business_type", TradeOrderType.Ordinary.name())
                    .eq("direction", direction)
                    .in("order_goods_id", orderGoodsIds)
                    .queryForList(String.class);
            if (!CollectionUtils.isEmpty(tradeIds)) {
                writeBackTradeIds.addAll(tradeIds);
            }
        });
        //关闭进行中的发票单
        List<String> invoiceIds = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL).selectField("invoice_apply_id")
                .in("trade_id", writeBackTradeIds)
                .queryForList(String.class);
        if (!CollectionUtils.isEmpty(invoiceIds)){
            //审核中，待开票，待收票，已开票
            List<InvoiceStatus> statuss = Arrays.asList(InvoiceStatus.Approving, InvoiceStatus.DrawerConfirming, InvoiceStatus.ReceiverConfirming, InvoiceStatus.Done);
            invoiceApplyService.close(invoiceIds, statuss);
            //关闭对应结算单
            settleService.closeByTrade(writeBackTradeIds);
        }
        tradeService.writeBackAllowedInvoiceQuantity(writeBackTradeIds);
        log.info("结束创建商品退货账单");
    }

    private Collection<Bill> refundNonExist(Collection<Bill> params) {
        if (CollectionUtils.isEmpty(params)) {
            return Collections.emptyList();
        }
        params.forEach(t -> t.setType(BillType.Refund.name()).setOffset_quantity(t.getQuantity()));

        //如果存在部分核销，生成退款账单拆分生成
        Collection<Bill> bills = new ArrayList<>();
        for (Bill param : params) {
            //已核销的数量=总数-可核销的数量
            BigDecimal offsetQuantityDone = BigDecimalUtils.sub(param.getQuantity(), param.getOffset_quantity());

            if (offsetQuantityDone.compareTo(BigDecimal.ZERO) != 0) {
                Bill offsetBillDone = Bill.builder().build();
                Bill offsetBillUndo = Bill.builder().build();
                BeanUtils.copyProperties(param, offsetBillDone);
                BeanUtils.copyProperties(param, offsetBillUndo);

                bills.add(offsetBillDone.setQuantity(offsetQuantityDone));
                bills.add(offsetBillUndo.setQuantity(param.getOffset_quantity()));
            } else {
                bills.add(param);
            }
        }
        bills = buildBill(bills);
        //持久化账单数据
        BoServiceUtils.createBo(ModelConst.T_BILL, bills.stream().map(BeanUtil::beanToMap).collect(Collectors.toList()));
        return bills;
    }
    /**
     * 根据订单已生成的账单，构建退款账单
     *
     * @param params 退款账单参数
     * @return 构建退款账单
     */
    private List<Bill> refundExist(Collection<Bill> params) {

        List<Bill> results = new ArrayList<>();
        if (CollectionUtils.isEmpty(params)) {
            return results;
        }

        //查询已收货账单
        List<Bill> bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                .in("order_goods_id", params.stream().map(Bill::getOrder_goods_id).collect(Collectors.toSet()))
                .in("direction", params.stream().map(Bill::getDirection).collect(Collectors.toList()))
                .eq("type", BillType.Goods.name())
                .queryForPOJOList(Bill.class);
        Assert.notEmpty(bills, "不存在已收货账单");

        Map<String, List<Bill>> billByTradeGroup = bills.stream().collect(Collectors.groupingBy(Bill::getTrade_id));
        billByTradeGroup.forEach((tradeId, billValues) -> {
            BigDecimal quantity = BigDecimal.ZERO, total = BigDecimal.ZERO;
            for (Bill billValue : billValues) {
                quantity = BigDecimalUtils.add(billValue.getQuantity(), quantity);
                total = BigDecimalUtils.add(billValue.getTotal(), total);
            }

            BigDecimal offsetRefundQuantity = BigDecimal.ZERO, offsetRefundTotal = BigDecimal.ZERO;
            //交易已完成退货收货的数量
            String offsetCompleteSql = "select IFNULL(sum(quantity),0) quantity, IFNULL(sum(total),0) total from "+JdbcTemplateUtils.getTableName(ModelConst.T_BILL)+" where trade_id=:tradeId" +
                    " and deleted = 0 and type='Refund' and  offset_quantity = 0 ";
            Bill offsetRefund =  JdbcTemplateUtils.queryForPOJOList(offsetCompleteSql, Maps.newHashMap("tradeId", tradeId), ModelConst.T_BILL, Bill.class).get(0);
            if (null != offsetRefund) {
                offsetRefundQuantity = null == offsetRefund.getQuantity() ? BigDecimal.ZERO : offsetRefund.getQuantity();
                offsetRefundTotal = null == offsetRefund.getTotal() ? BigDecimal.ZERO : offsetRefund.getTotal();
            }

            Bill bill = billValues.get(0);

            BigDecimal canRefundQuantity = BigDecimalUtils.sub(quantity, offsetRefundQuantity);
            List<Bill> refundBills = params.stream()
                    .filter(t -> t.getOrder_goods_id().equals(bill.getOrder_goods_id())
                            && t.getDirection().equals(bill.getDirection()))
                    .collect(Collectors.toList());

            BigDecimal refundQuantity = BigDecimal.ZERO;
            for (Bill refundBill : refundBills) {
                refundQuantity = BigDecimalUtils.add(refundBill.getQuantity(), refundQuantity);
            }
            Assert.isTrue(refundQuantity.compareTo(canRefundQuantity) == 1, "退款数量大于可退款数量");

            Trade trade = tradeService.loadById(tradeId);
            BigDecimal price = trade.getPrice();
            BigDecimal refundTotal = BigDecimalUtils.round(BigDecimalUtils.mul(refundQuantity, price), 2);
            if (refundQuantity.compareTo(canRefundQuantity) == 0) {
                refundTotal = BigDecimalUtils.sub(total, offsetRefundTotal);
                price = BigDecimalUtils.div(refundTotal, refundQuantity);
            }

            Bill build = Bill.builder()
                    .trade_id(bill.getTrade_id())
                    .direction(bill.getDirection())
                    .business_type(bill.getBusiness_type())
                    .customer_order_id(bill.getCustomer_order_id())
                    .vendor_order_id(bill.getVendor_order_id())
                    .order_goods_id(bill.getOrder_goods_id())
                    .organization_id(bill.getOrganization_id())
                    .type(BillType.Refund.name())
                    .summary(bill.getSummary())
                    .bill_date(new Date())
                    .organization_name(bill.getOrganization_name())
                    .quantity(refundQuantity)
                    .offset_quantity(BigDecimal.ZERO)
                    .price(price)
                    .tax_rate(bill.getTax_rate())
                    .total(refundTotal)
                    .build();
            BigDecimal verifyTotal = BigDecimalUtils.mul(build.getPrice(), build.getQuantity()).setScale(2, RoundingMode.HALF_UP);
            Assert.isTrue(verifyTotal.compareTo(build.getTotal()) == 0, "单价乘以数量不等于总金额");
            results.add(build);
        });
        if(!CollectionUtils.isEmpty(results)){
            BoServiceUtils.createBo(ModelConst.T_BILL, results.stream().map(BeanUtil::beanToMap).collect(Collectors.toList()));
        }
        return results;
    }



    /**
     * 创建服务费账单
     * @param serviceBills
     * @return 账单ID
     */
    public List<String> createServiceBill(List<Bill> serviceBills){
        //获取服务费账单对应的原账单
        List<String> strings = serviceBills.stream().map(Bill::getTarget_id).collect(Collectors.toList());
        List<Bill> billList = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                .in("id", strings)
                .queryForPOJOList(Bill.class);

        //判断原结算单明细是否创建过服务费账单
        List<String> referenceIds = serviceBills.stream().filter(t -> null != t.getReference_id()).map(Bill::getReference_id).collect(Collectors.toList());
        Integer count = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField(" count(1) ")
                .in("reference_id", referenceIds)
                .in("type", Arrays.asList(BillType.PlatformFee.name(), BillType.FinancialFee.name()))
                .queryForObject(Integer.class);
        Assert.isTrue(count == 0, "referenceIds【" + StringUtils.join(referenceIds, ",") + "】已存在");

        List<Bill> persistBillList = new ArrayList<>();
        for (Bill param : serviceBills) {
            Assert.notNull(param.getReference_id(), "referenceId不能为空");

            //匹配交易列表trades中的数据
            Bill bill = billList.stream().filter(t -> param.getTarget_id().equals(t.getId())).findAny().get();

            persistBillList.add(Bill.builder()
                    .trade_id(bill.getTrade_id())
                    .target_id(bill.getId())
                    .quantity(BigDecimal.ONE)
                    .offset_quantity(BigDecimal.ZERO)
                    .price(param.getTotal())
                    .direction(bill.getDirection())
                    .business_type(bill.getBusiness_type())
                    .reference_id(param.getReference_id())
                    .customer_order_id(bill.getCustomer_order_id())
                    .vendor_order_id(bill.getVendor_order_id())
                    .order_goods_id(bill.getOrder_goods_id())
                    .organization_id(bill.getOrganization_id())
                    .type(param.getType())
                    .summary(bill.getSummary())
                    .bill_date(new Date())
                    .tax_rate(PlatformConst.PLATTAXRATE)
                    .organization_name(bill.getOrganization_name())
                    .total(param.getTotal())
                    .build());
        }
        List<Map<String, Object>> data = persistBillList.stream().map(BeanUtil::beanToMap).collect(Collectors.toList());
        BoServiceUtils.createBo(ModelConst.T_BILL, data);
        return data.stream().map(t -> t.get("id").toString()).collect(Collectors.toList());
    }

    @Override
    public List<String> createBlanceBill(List<Bill> balanceBills) {
        //TODO 参数验证
        Collection<Bill> persistBills = new ArrayList<>();

        List<String> ids = balanceBills.stream().map(Bill::getTrade_id).collect(Collectors.toList());
        List<Trade> trades = JdbcTemplateUtils.builder(ModelConst.T_TRADE).selectField("*")
                .in("id", ids)
                .queryForPOJOList(Trade.class);

        Assert.isTrue(!CollectionUtils.isEmpty(trades),"未获取到对应的交易信息");

        for (Bill param : balanceBills) {
            //匹配交易列表trades中的数据
            Trade trade = trades.stream().filter(t -> param.getTrade_id().equals(t.getId())).findAny().get();
            Date billDate = new Date();
            if (null != param.getBill_date()) {
                billDate = param.getBill_date();
            }

            persistBills.add(Bill.builder()
                    .trade_id(trade.getId())
                    .direction(trade.getDirection())
                    .business_type(trade.getBusiness_type())
                    .customer_order_id(trade.getCustomer_order_id())
                    .vendor_order_id(trade.getVendor_order_id())
                    .order_goods_id(trade.getOrder_goods_id())
                    .organization_id(trade.getOrganization_id())
                    .type(BillType.Balance.name())
                    .summary(JSON.toJSONString(trade.getSummary()))
                    .bill_date(billDate)
                    .organization_name(trade.getOrganization_name())
                    .total(param.getTotal())
                    //补差是对金额进行补差，所以数量=1、单价=补差金额、税率=0
                    .price(param.getTotal())
                    .quantity(BigDecimal.ONE)
                    .offset_quantity(BigDecimal.ZERO)
                    .tax_rate(BigDecimal.ZERO)
                    .build());
        }
        List<Map<String, Object>> params = persistBills.stream().map(BeanUtil::beanToMap).collect(Collectors.toList());
        BoServiceUtils.createBo(ModelConst.T_BILL, params);
        return params.stream().map(t->t.get("id").toString()).collect(Collectors.toList());
    }

    /**
     * 获取账单数据，通过Actual参数判断是否要减掉退货退款和退服务费部分
     * @param query
     * @return
     */
    @Override
    public List<Bill> list(BillQuery query){
        MapBuilder<String, Object> param = MapBuilder.create(new HashMap<>());
        StringBuffer sql = new StringBuffer("select * from "+JdbcTemplateUtils.getTableName(ModelConst.T_BILL));
        sql.append(" where deleted = 0");
        if(!ObjectUtils.isEmpty(query)){
            if(!CollectionUtils.isEmpty(query.getTrade_id())){
                sql.append(" and trade_id in (:trade_id)");
                param.put("trade_id", query.getTrade_id());
            }
            if(!CollectionUtils.isEmpty(query.getIds())){
                sql.append(" and id in (:id)");
                param.put("id", query.getIds());
            }
            if(StringUtils.isNotBlank(query.getType())){
                sql.append(" and type = :type");
                param.put("type", query.getType());
            }
        }
        sql.append(" order by id desc");
        List<Bill> results = JdbcTemplateUtils.queryForPOJOList(sql.toString(), param.build(), ModelConst.T_BILL, Bill.class);
        if (query.getActual()) {
            List<Bill> afterBills = new ArrayList<>();
            List<Bill> goodsBills = results.stream().filter(t -> BillType.Goods.name().equals(t.getType())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(goodsBills)) {
                afterBills.addAll(this.goodsFilterRefund(goodsBills));
            }

            List<Bill> feeBills = results.stream().filter(t -> BillType.getFee().contains(Enum.valueOf(BillType.class, t.getType()))).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(feeBills)) {
                afterBills.addAll(this.feeFilterRefund(feeBills));
            }

            //添加排除服务费 & 商品账单的其它账单
            List<BillType> excludeBillType = Arrays.asList(BillType.PlatformFee, BillType.FinancialFee, BillType.Goods);
            List<Bill> otherBills = results.stream().filter(t -> !excludeBillType.contains(Enum.valueOf(BillType.class, t.getType()))).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(otherBills)) {
                afterBills.addAll(otherBills);
            }

            results = afterBills;
        }
        return results;

    }

    /**
     * 货物类型账单过滤已全部退款的账单并减去已退的数量
     *
     * @param goodsBills 账单实体列表
     * @return 减去退款数量和实体列表
     */
    public List<Bill> goodsFilterRefund(List<Bill> goodsBills){
        Set<String> tradeIds = goodsBills.stream().map(Bill::getTrade_id).collect(Collectors.toSet());
        MapBuilder<String, Object> param = MapBuilder.create(new HashMap<>());
        for (String tradeId : tradeIds) {
            param.map().clear();
            //根据交易获取所有退货账单
            String refundSql = "select * from " + JdbcTemplateUtils.getTableName(ModelConst.T_BILL)
                    + " where deleted = 0 and trade_id=:trade_id and type ='Refund' and offset_quantity != quantity";
            List<Bill> allRefundBillsList = JdbcTemplateUtils.queryForPOJOList(refundSql, param.put("trade_id", tradeId).build(), ModelConst.T_BILL, Bill.class);

            if (CollectionUtils.isEmpty(allRefundBillsList)) {
                continue;
            }

            for (Bill refundBill : allRefundBillsList) {
                //根据交易id得到所有收货账单
                String noRefundSql = "select * from " + JdbcTemplateUtils.getTableName(ModelConst.T_BILL)
                        + " where deleted = 0 and trade_id=:trade_id and type <> 'Refund' and offset_quantity != quantity order by createdTime ";
                List<Bill> allBillsList = JdbcTemplateUtils.queryForPOJOList(noRefundSql, param.put("trade_id", tradeId).build(), ModelConst.T_BILL, Bill.class);

                BigDecimal allRefundBillQuantity = refundBill.getQuantity(), allRefundBillTotal = refundBill.getTotal();
                if (refundBill.getOffset_quantity().compareTo(BigDecimal.ZERO) == 1) {
                    //需要排除退未收货的数量
                    allRefundBillQuantity = refundBill.getQuantity().subtract(refundBill.getOffset_quantity());
                    allRefundBillTotal = allRefundBillQuantity.multiply(refundBill.getPrice()).setScale(2, RoundingMode.HALF_UP);
                }
                //按照时间排序后的入参的账单，抵消退款
                for (Bill billEntity : allBillsList) {
                    if (allRefundBillQuantity.compareTo(BigDecimal.ZERO) == 0) {
                        break;
                    }
                    //还有收货数量去抵消退货数量
                    if (billEntity.getQuantity().compareTo(allRefundBillQuantity) >= 0) {
                        billEntity.setQuantity(billEntity.getQuantity().subtract(allRefundBillQuantity))
                                .setTotal(billEntity.getTotal().subtract(allRefundBillTotal));
                        //退货抵消完
                        allRefundBillQuantity = BigDecimal.ZERO;
                        allRefundBillTotal = BigDecimal.ZERO;
                    } else {
                        allRefundBillQuantity = allRefundBillQuantity.subtract(billEntity.getQuantity());
                        allRefundBillTotal = allRefundBillTotal.subtract(billEntity.getTotal());

                        billEntity.setQuantity(BigDecimal.ZERO).setTotal(BigDecimal.ZERO);
                    }
                }

                //把按时间排序并且抵消退货的账单，数据整理到入参里面
                for (Bill billEntity : goodsBills) {
                    List<Bill> billEntityAfterOrderByList = allBillsList.stream().filter(t -> t.getId().equals(billEntity.getId())).distinct().collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(billEntityAfterOrderByList)) {
                        billEntity.setQuantity(billEntityAfterOrderByList.get(0).getQuantity())
                                .setTotal(billEntityAfterOrderByList.get(0).getTotal());
                    }
                }
            }
        }
        return goodsBills.stream().filter(t -> t.getQuantity().compareTo(BigDecimal.ZERO) != 0).collect(Collectors.toList());
    }

    /**
     * 服务费类型账单过滤已全部退款的账单并减去已退的金额
     *
     * @param bills 账单实体列表
     * @return 减去退款金额和实体列表
     */
    protected List<Bill> feeFilterRefund(List<Bill> bills) {
        Set<String> billIds = bills.stream().map(Bill::getId).collect(Collectors.toSet());
        MapBuilder<String, Object> param = MapBuilder.create(new HashMap<>());
        String noOffsetsql = "select * from " + JdbcTemplateUtils.getTableName(ModelConst.T_BILL)
                + " where deleted = 0 and target_id in (:billIds) and type in (:types) and offset_quantity = 0";
        param.put("billIds", billIds);
        param.put("types", Arrays.asList(BillType.PlatformFeeRefund.name(), BillType.FinancialFeeRefund.name()));
        List<Bill> refundBills = JdbcTemplateUtils.queryForPOJOList(noOffsetsql, param.build(), ModelConst.T_BILL, Bill.class);

        if (CollectionUtils.isEmpty(refundBills)) {
            return bills;
        }

        for (Bill bill : bills) {
            List<Bill> refundBillsMapping = refundBills.stream().filter(t -> t.getTarget_id().equals(bill.getId())).collect(Collectors.toList());
            BigDecimal refundTotal = CalculatorUtils.sum(refundBillsMapping, BiFunctionConst.billTotalFun);
            bill.setTotal(bill.getTotal().subtract(refundTotal).setScale(2,RoundingMode.HALF_UP));
        }

        return bills.stream().filter(t -> t.getTotal().compareTo(BigDecimal.ZERO) != 0).collect(Collectors.toList());
    }

    /**
     * 申请规则验证，业务类型、业务方向必须一致，账单类型必须只能是商品和增补或平台费和金融费
     *
     * @param bills     账单列表
     * @param verifyOrg 是否验证组织唯一
     */
    @Override
    public void applyRuleValid(List<Bill> bills, boolean verifyOrg) {
        Bill firstBill = bills.get(0);
        //业务类型
        String businessType = firstBill.getBusiness_type();
        //业务方向
        String direction = firstBill.getDirection();
        SelectorFormat organizationId = firstBill.getOrganization_id();
        for (Bill bill : bills) {
            if (verifyOrg) {
                Assert.isTrue(bill.getOrganization_id().getId().equals(organizationId.getId()), "目标组织必须唯一");
            }
            Assert.isTrue(businessType.equals(bill.getBusiness_type()), "业务类型必须一致");
            Assert.isTrue(direction.equals(bill.getDirection()), "业务方向必须一致");
        }
    }

    @Override
    public List<Bill> listServiceFee(BillQuery query) {
        String sql = "SELECT " +
                " tb.ID, tb.trade_id, tb.target_id, tb.reference_id, tb.customer_order_id, tb.vendor_order_id, tb.order_goods_id, tb.organization_id, tb.TYPE, tb.business_type, " +
                " tb.direction, tb.summary, tb.bill_date, tb.organization_name, tb.quantity, tb.offset_quantity, tb.price, tb.tax_rate, tb.total, trade.platform_organization_name " +
                "FROM " +
                " i_financialcenter_t_bill tb " +
                " left join i_financialcenter_t_trade trade on trade.id = tb.trade_id "+
                " LEFT JOIN ( " +
                " SELECT " +
                "  invoice_detail_bill.bill_id bill_id, " +
                "  IFNULL((SUM(invoice_detail_bill.total) - SUM(invoice_detail_bill.offset_total)), 0) total  " +
                " FROM " +
                "  i_financialcenter_t_invoice_detail_bill invoice_detail_bill " +
                "  LEFT JOIN i_financialcenter_t_invoice_apply apply ON apply.ID = invoice_detail_bill.invoice_apply_id  " +
                " WHERE " +
                "  invoice_detail_bill.deleted = 0  " +
                "  AND apply.deleted = 0  " +
                "  AND apply.status <> 'Cancel'  " +
                "  AND apply.business_type = 'Service'  " +
                " GROUP BY " +
                "  invoice_detail_bill.bill_id  " +
                " ) invoice_detail_bill ON invoice_detail_bill.bill_id = tb.ID  " +
                "WHERE " +
                " tb.deleted = 0  " +
                " AND invoice_detail_bill.total <> tb.total  " +
                " AND ( tb.TYPE = 'FinancialFee' OR tb.TYPE = 'PlatformFee' ) " +
                " AND JSON_UNQUOTE(trade.platform_organization_id -> '$.id') in (:platformOrgId)" +
                " AND tb.organization_id in (:orgId) ";

        Map<String, List<String>> param = Maps.newHashMap("orgId", query.getOrganization_ids());
        List<String> platformOrganizationIds = StringUtils.isNotEmpty(query.getPlatform_organization_id()) ? Collections.singletonList(query.getPlatform_organization_id()):PlatformConst.PLATFORM_ORGANIZATION_IDS;
        param.put("platformOrgId", platformOrganizationIds);
        List<Bill> billList = jdbcTemplate.query(sql, param, BeanPropertyRowMapper.newInstance(Bill.class));
        if (CollectionUtils.isEmpty(billList)){
            return billList;
        }

        Set<String> billIds = billList.stream().map(Bill::getId).collect(Collectors.toSet());
        List<Bill> refundBills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                .in("target_id", billIds)
                .in("type",Arrays.asList(BillType.PlatformFeeRefund.name(), BillType.FinancialFeeRefund.name()))
                .gt("offset_quantity", BigDecimal.ZERO)
                .queryForList(Bill.class);
        if (!CollectionUtils.isEmpty(refundBills)){
            for (Bill bill:billList){
                List<Bill> refundBillsMapping = refundBills.stream().filter(t -> t.getTrade_id().equals(bill.getId())).collect(Collectors.toList());
                BigDecimal refundTotal = CalculatorUtils.sum(refundBillsMapping, BiFunctionConst.billTotalFun);
                bill.setTotal(BigDecimalUtils.round(BigDecimalUtils.sub(bill.getTotal(), refundTotal), 2));
            }
        }
        billList = billList.stream().filter(t->t.getTotal().compareTo(BigDecimal.ZERO) != 0).collect(Collectors.toList());

        //获取服务费账单原结算信息
        sql = "select detail.id, detail.total settle_amount, settle.payment_time settle_time, detail.settle_id settle_ids, invoice.number invoice_number " +
                "   from i_financialcenter_t_settle_detail detail " +
                "   left join i_financialcenter_t_settle settle on settle.id=detail.settle_id " +
                "   left join i_financialcenter_t_invoice_apply invoice on invoice.id=detail.invoice_apply_id " +
                "   where detail.deleted=0 and detail.id in (:settleDetailIds) ";
        List<Map<String, Object>> settleList = jdbcTemplate.queryForList(sql, Maps.newHashMap("settleDetailIds", billList.stream().map(Bill::getReference_id).collect(Collectors.toList())));

        for (Bill bill : billList){
            Map<String, Object> map = settleList.stream().filter(t -> t.get("id").toString().equals(bill.getReference_id())).findAny().orElse(new HashMap<>());
            if (CollectionUtils.isEmpty(map)){
                continue;
            }
            bill.setInvoice_number(map.get("invoice_number").toString());
            bill.setSettle_ids(map.get("settle_ids").toString());
            bill.setSettle_amount(new BigDecimal(map.get("settle_amount").toString()));
            bill.setSettle_time(map.get("settle_time").toString());

        }
        return billList;
    }

    /**
     * 删除补差账单
     *
     * @param ids 账单ID
     */
    @Override
    public void delete(Collection<String> ids) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("删除补差账单");

        List<Bill> balanceBills = list(BillQuery.builder().type(BillType.Balance.name()).actual(false).ids(ids).build());

        Assert.isTrue(!CollectionUtils.isEmpty(balanceBills), "未找到要删除的补差账单");

        Assert.isTrue(balanceBills.size() == ids.size(), "需要删除的和实际要删除的不符");
        MapBuilder<String, Object> param = MapBuilder.create(new HashMap<>());
        for (String id : ids) {
            param.map().clear();
            param.put("id", id);
            param.put("deleted", 1);
            BoServiceUtils.updateMainBo( ModelConst.T_BILL, param.build());
        }
        stopWatch.stop();
        String prettyPrint = stopWatch.prettyPrint();
        log.info(prettyPrint);
    }

}
